from math import floor

from kivy.logger import Logger
from kivy.metrics import inch

class SpaceModel(object):
    """
    This is a 2 dimensional space.
    type: width_inches: float, int
    type: height_inches: float, int
    """
    def __init__(self, width_inches, height_inches):
        self.width_inches = width_inches
        self.height_inches = height_inches
        

class OutdoorSpaceModel(SpaceModel):
    """
    This is a 2 dimensional space on an outdoor target.
    The space also has a distance in yards.
    """
    def __init__(self, width_inches, height_inches, distance_yards, description):
        """
        type: width_inches: float, int
        type: height_inches: float, int
        type: distance_yards: float, int
        type: description: str
        """
        SpaceModel.__init__(self, width_inches, height_inches)
        self.distance_yards = distance_yards
        self.description = description


class DryFireSpaceModel(object):
    """
    This is a space on an indoor target.
    The space has a cooresponding OutdoorSpaceModel.
    The indoor target has a distance in feet.
    The space has a length in inches.
    """
    def __init__(self, outdoor_space_model, distance_feet):
        """
        type: outdoor_space_model: OutdoorSpaceModel
        type: distance_feet: float, int
        """
        self._outdoor_space_model = outdoor_space_model
        self._distance_feet = distance_feet
        self._height_pixels = 0
        self._width_pixels = 0
        self._pixels_per_inch = inch(1)
        self._recalc()

    # properties

    @property
    def distance_yards(self):
        return self._outdoor_space_model.distance_yards

    @distance_yards.setter
    def distance_yards(self, value):
        """
        type: value: float, int
        """
        if self._distance_yards != value:
            self._distance_yards = value
            self._recalc()
        

    @property
    def description(self):
        return self._outdoor_space_model.description

    @property
    def distance_feet(self):
        return self._distance_feet

    @distance_feet.setter
    def distance_feet(self, value):
        """
        type: value: float, int
        """
        if self._distance_feet != value:
            self._distance_feet = value
            self._recalc()

    @property
    def pixels_per_inch(self):
        return self._pixels_per_inch

    @pixels_per_inch.setter
    def pixels_per_inch(self, value):
        """
        type: value: float, int
        """
        if self._pixels_per_inch != value:
            self._pixels_per_inch = value
            self._recalc()

    @property
    def width_inches(self):
        return self._outdoor_space_model.width_inches

    @property
    def width_pixels(self):
        return self._width_pixels

    @property
    def height_inches(self):
        return self._outdoor_space_model.height_inches

    @property
    def height_pixels(self):
        return self._height_pixels

    # public methods

    def recalc(self, pixels_per_inch=None, indoor_distance_feet=None, outdoor_distance_yards=None):
        if pixels_per_inch is not None:
            self._pixels_per_inch = pixels_per_inch
        if indoor_distance_feet is not None:
            self._distance_feet = indoor_distance_feet
        if outdoor_distance_yards is not None:
            self._outdoor_space_model.distance_yards = outdoor_distance_yards
        self._recalc()

    # private methods

    def _w_h_moa(self):
        """
        returns a tuple of width moa and height moa.
        :rtype : (float, float)
        """
        # outdoor_magnification of the target compared to 100 yards.
        outdoor_magnification = 100.0 / self._outdoor_space_model.distance_yards
        # calc width in moa at 100 yards
        # 1.05 the number of inches in 1 moa at 100 yards
        moa_at_100_yds = (self._outdoor_space_model.width_inches / 1.05)
        # use the magnification to get the moa of the outdoor target's height at its distance.
        w_moa = moa_at_100_yds * outdoor_magnification
        # calc height in moa at 100 yards
        # 1.05 the number of inches in 1 moa at 100 yards
        moa_at_100_yds = (self._outdoor_space_model.height_inches / 1.05)
        # use the magnification to get the moa of the outdoor target's height at its distance.
        h_moa = moa_at_100_yds * outdoor_magnification
        return w_moa, h_moa

    def _recalc(self):
        # get the targets width and height in moa
        w_moa, h_moa = self._w_h_moa()
        # 0.0035 = number of inches in 1 moa at 1 foot.
        # 1.05 / 300 = 0.0035
        # 1.05 the number of inches in 1 moa at 100 yards
        # 300 is the number of feet in 100 yards.
        # calc what 1 moa will be in inches for the graphic
        graphic_inches_per_moa = 0.0035 * self._distance_feet
        # calc the height of the graphic.
        # the graphic will display the same moa as the outdoor target.
        # so convert the moa to inches and then to pixels
        graphic_inches = graphic_inches_per_moa * h_moa
        pixels = self._pixels_per_inch * graphic_inches
        self._height_pixels = int(floor(pixels))
        # calc the width of the graphic.
        # the graphic will display the same moa as the outdoor target.
        # so convert the moa to inches and then to pixels.
        graphic_inches = graphic_inches_per_moa * w_moa
        pixels = self._pixels_per_inch * graphic_inches
        self._width_pixels = int(floor(pixels))
